#pragma once

#include <iostream>
#include <cstdlib>
#include <cassert>

// setup the search agent object
void setup_search_agent(I_SEARCH_AGENT s)
{
	clear_table_transposition(s.trans);
	clear_table_history(s.hist);
};

// driver for the alphabeta search
I_CHESS_MOVE* alpha_beta_driver(I_CHESS_POSITION* p, int depth)
{
	// create return value
	I_CHESS_MOVE* mv = (I_CHESS_MOVE*) malloc(sizeof(I_CHESS_MOVE));
	
	// check bounds, to ensure that we are not trying to search too deeply
	// NOTE: we don't put this in alpha_beta() because we want to allow search extensions
	trace_assert(depth <= ALPHABETA_SAFETY_CUTOFF, "INVALID alpha-beta depth. Reduce the depth of search.");
	
	// check bounds, to ensure that we have at least a single ply of search
	trace_assert(depth > 0, "INVALID alpha-beta depth. Depth must be greater than zero.");
	
	// run alphabeta
	alpha_beta(p, I_ALPHABETA_MINVAL, I_ALPHABETA_MAXVAL, mv, depth);
	
	// return value
	return mv;
};

// alpha-beta search
int alpha_beta(I_CHESS_POSITION *p, int alpha, int beta, I_CHESS_MOVE* best_move, int depth)
{
	I_CHESS_MOVE* curr_move = (I_CHESS_MOVE*) malloc(sizeof(I_CHESS_MOVE));
	int i; int move_value, best_value = I_ALPHABETA_MINVAL;
	
	// if the other player is in check, this is not a legal path to follow
	if (in_check(p, p->col ^ 1)) return I_ALPHABETA_MINVAL;
	
	// if we have searched as many iterations as specified, start quiescence
	if (depth == 0) return quiescence(p, alpha, beta);
	
	// generate the movelist
	MOVELIST moves = gen_move_list(p);

	// if there are no legal moves, just check to see how good this position is
	if (moves.size == 0) return GAME.SEARCH_AGENT.eval_func(p);
	
	// for all moves in the move list
	for (i = 0; i < moves.size; ++i)
	{
		// determine the move value (recurse) for this move choice
		move_value = -alpha_beta(moves.arr[i]->board[MOVE_TO], -beta, -alpha, best_move, depth - 1);
		
		// if this move is better than our beta cutoff, then we don't expect to see anything better
		if (move_value > best_value) if ((best_value = move_value) >= beta)
		{
			//trans_save(p, best_value, EVALTYPE_BOUNDARY, depth);
			add_count(moves.arr[i]);
			
			break;
		}
		
		// if the best value is better than the alpha, store the max value and associated move
		if (best_value > alpha)
		{
			alpha = best_value;
			memcpy(curr_move, moves.arr[i], sizeof(I_CHESS_MOVE));
		};
	}
	
	// store the board (accurate)
	//trans_save(p, best_value, EVALTYPE_ACCURATE, depth);
	
	// make a copy of the best move
	memcpy(best_move, curr_move, sizeof(I_CHESS_MOVE));
	
	// free up memory
	free(curr_move); free_move_list(moves);
	
	// return the best move
	return best_value;
};

// quiescence search
int quiescence(I_CHESS_POSITION *p, int alpha, int beta)
{
	return GAME.SEARCH_AGENT.eval_func(p);
};
